#!/usr/bin/env python
# encoding: utf-8

import socket
import logging
import optparse
import sys, time
import os.path, os
import parse
from imp_protocolo import *
from constants import *
from conexion_cliente import *


""" Este modulo implementa el servidor. Se define la clase servidor y sus meto-
    dos correspondientes, siendo el mas importante server_run que pone a "co-
    rrer" el server. Se ocupa tambien de construir y enviar las respuestas en
    caso de error. En cambio, si los pedidos se procesan correctamente, las
    mismas son construidas por las propias funciones, definidas en el modulo
    imp_protocolo, pero sigue siendo el servidor quien las envia.
"""



class Server(object):
    def __init__(self, directorio, port=default_port):
        """ Crea una nueva instancia de la clase server. La nueva instancia se
            liga al puerto pasado como argumento. El directorio pasado como ar-
            gumento es el que el server va a poner a disposicion del cliente
        """
        self.sckt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sckt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # La linea de arriba es la que permite ligar enseguida un servidor al
        # puerto y no tener que esperar alrededor de un minuto
        self.sckt.bind(('', port)) #'' es un nombre simbólico
        self.sckt.listen(1) # máximo número de conexiones es 1 
        self.connected = False
        self.wd = directorio
        self.cliente_actual = None
 
    def server_run(self):
        """ Pone a correr un server, para con una interrupcion de teclado"""
        while True:
            socket = self.sckt.accept()[0]
            self.cliente_actual = Conexion_cliente(socket)
            self.connected=True
            peticiones_incorrectas = 0
            while self.connected:
                if not self.cliente_actual.buffer.isEmpty():
                    """ El buffer no está vacío. Nos fijamos si es un mensaje 
                    completo """
                    data = self.cliente_actual.buffer.devolver_mensaje()
                    self.cliente_actual.buffer.vaciar()
                    (comando, codigo_error) = parse.parsear(data,\
                                                    self.cliente_actual.buffer)
                    if comando == None and codigo_error == code_ok:
                        #Entonces lo que estaba en el buffer era un comando
                        #incompleto. Recibimos del cliente lo que falta. Le
                        #damos un tiempo limitado para completar la peticion
                        data = self.cliente_actual.recibir_mensaje(True,\
                                                                   time_limit)
                        if not data == None:
                            data = self.cliente_actual.buffer.devolver_mensaje\
                                   () + data
                            self.cliente_actual.buffer.vaciar()
                            (comando, codigo_error) = parse.parsear(data, \
                                                     self.cliente_actual.buffer)
                        else:
                        #Expiro el tiempo de espera
                            self._send_error(time_expired)
                else:
                    data = self.cliente_actual.recibir_mensaje(False, None)
                    (comando, codigo_error) = parse.parsear(data,\
                                                self.cliente_actual.buffer)

                if codigo_error == code_ok and not comando == None:
                    true_comando = comando.get_command()
                    if true_comando == "quit":
                        quit(self.cliente_actual)
                        self.connected = False
                    elif true_comando == "get_file_listing":
                        print str(self.wd)
                        get_file_listing(self.cliente_actual, self.wd)
                    elif true_comando == "get_metadata":
                        archivo = comando.get_argn(1)
                        res = get_metadata(self.cliente_actual, self.wd,\
                                           archivo)
                        if res == file_not_found:
                            self._send_error(file_not_found)
                    elif true_comando == "get_slice":
                        archivo = comando.get_argn(1)
                        offset = comando.get_argn(2)
                        length = comando.get_argn(3)
                        res = get_slice(self.cliente_actual, self.wd,\
                                        archivo, offset, length)
                        if res == file_not_found:
                            self._send_error(file_not_found)
                        elif res == bad_offset:
                            self._send_error(bad_offset)
                elif codigo_error != code_ok:
                    peticiones_incorrectas += 1
                    if peticiones_incorrectas == bad_request_limit:
                        self._send_error(bad_request_limit_error)
                    self._send_error(codigo_error)


    def close_server(self):
        self.sckt.close()

    def _send_error(self, error_code):
        """ Funcion "privada" de la clase server. Envia un mensaje al cliente
            con el formato correspondiente según el código de error """

        answer = str(error_code) + " " + error_messages[error_code] + eol       
        self.cliente_actual.enviar_mensaje(answer)

        if fatal_status(error_code):
            self.connected = False
            self.cliente_actual.cerrar_conexion()

def main():
    """
    Interfaz interactiva para el servidor. Permite elegir el directorio y
    el puerto. Para salir presionar CTRL+d
    """

    cwd = os.getcwd()
    # Parsear argumentos
    parser = optparse.OptionParser(usage="%prog [options]")
    parser.add_option("-d", "--directorio",
                      help=u"Directorio a publicar por el server", default=cwd)
    parser.add_option("-p", "--port",
                      help=u"Puerto donde escuchar", default=default_port)

    options, args = parser.parse_args()
    try:
        port = int(options.port)
        directorio = str(options.directorio)
        directorio=cwd+"/"+directorio
        print directorio
    except ValueError:
        sys.stderr.write("Número de puerto inválido: %s\n" % str(options.port)) 
        parser.print_help()
        sys.exit(1)

    server = Server(directorio, port)
    try:
        server.server_run()   

    except KeyboardInterrupt:
        sys.stderr.write("Desconectando por voluntad propia!\n")
        if server.connected:
            quit(server.cliente_actual)
        server.close_server()
        sys.exit(0)
    """except:
        server.send_error(internal_error)"""

if __name__ == '__main__':
    main()
